#pragma once
#include <time.h>
#include <string.h>
#include <unordered_map>

const unsigned PROFILER_MAX_TIMER_COUNT     = 4096;
const unsigned PROFILER_MAX_STACK_LEVEL     = 20;
const unsigned PROFILER_MAX_TIMER_NAME_LEN  = 64;

const int PROFILER_NO_PARENT    = -1;
const int PROFILER_MULTI_PARENT = -2;

extern bool g_isOpenProfile ;

struct PROFILE_TIMER 
{
    int index;
    int parentIndex;
    int level;

#ifdef WIN32
    long long beginTime;
#else
    timespec beginTime;
#endif
    unsigned  sampleCount;
    long long sampleTime; // in nanosecond

    char name[PROFILER_MAX_TIMER_NAME_LEN];

    PROFILE_TIMER(const char* _name) 
	{
        Clear();
        strncpy(name, _name, sizeof(name));
        name[sizeof(name) - 1] = '\0';
    }

    PROFILE_TIMER() : PROFILE_TIMER("") {  }
    
	void SetName(const char* _name) 
	{
        strncpy(name, _name, sizeof(name));
        name[sizeof(name) - 1] = '\0';
    }

    void Clear() 
	{
        index = -1;
        parentIndex = PROFILER_NO_PARENT;
        level = -1;

        memset(&beginTime, 0, sizeof(beginTime));
        sampleCount = 0;
        sampleTime = 0;
    }
};

// pointer to timer must keep always valid, normally should be a static variable.
void BeginProfile(PROFILE_TIMER* timer); 
void EndProfile();

// for support profile main thread in multi thread program(ignore another, only main thread)
void SetProfileThreadID();
void BeginProfileWithThreadCheck(PROFILE_TIMER* timer);
void EndProfileWithThreadCheck();

void ResetAllProfileTimer();
bool OutputTopProfileTimer();
bool OutputProfileTimerByNames(std::unordered_map<std::string, double> names);

#define _PROFILER_MERGE_STRING2(x,y) x##y
#define _PROFILER_MERGE_STRING(x,y) _PROFILER_MERGE_STRING2(x,y)

#define BEGIN_PROFILE(name) { \
    static PROFILE_TIMER _PROFILER_MERGE_STRING(s_timer_, __LINE__)(name); \
    BeginProfile(&_PROFILER_MERGE_STRING(s_timer_, __LINE__))

#define END_PROFILE() EndProfile(); }

class ProfilerSegmentHelper 
{
public:
    ProfilerSegmentHelper(PROFILE_TIMER* timer) 
	{
        BeginProfile(timer);
    }
    ~ProfilerSegmentHelper() 
	{
        EndProfile();
    }
};
#define PROFILE_SEGMENT(name) \
    static PROFILE_TIMER  _PROFILER_MERGE_STRING(s_timer_, __LINE__)(name); \
    ProfilerSegmentHelper _PROFILER_MERGE_STRING(segment_timer, __LINE__)( \
        &_PROFILER_MERGE_STRING(s_timer_, __LINE__))
